using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Xml.Serialization;

namespace GraphAlgsPresentation.Core
{
    /// <summary>
    /// The class provides information of the graph edge.
    /// </summary>
	[Serializable]
    public class LCEdge
    {
        private bool updated;
        private bool marked;
        private LCVertex source;
        private LCVertex destination;
        private string label = string.Empty;
        private Font font = new Font("Arial", 10);
        private Color lineColor = Color.MidnightBlue;
        private Color labelColor = Color.Black;
        private double weight = 0;
        private bool directed = false;
        private float moveLabel = 10;
        private int arrowSize = 6;
        private DashStyle style = DashStyle.Solid;
        private string sourceName = string.Empty;
        private string destinationName = string.Empty;
        
        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="LCEdge"/> is updated.
        /// </summary>
        /// <value><c>true</c> if updated; otherwise, <c>false</c>.</value>
        [Browsable(false), XmlIgnore]
        public bool Updated
        {
            get { return updated; }
            set { updated = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="LCEdge"/> is marked.
        /// </summary>
        /// <value><c>true</c> if marked; otherwise, <c>false</c>.</value>
        [Browsable(false), XmlIgnore]
        public bool Marked
        {
            get { return marked; }
            set { marked = value; }
        }

        /// <summary>
        /// Gets or sets the source vertex.
        /// </summary>
        /// <value>The source vetex.</value>
        [Browsable(false), XmlIgnore]
        public LCVertex Source
        {
            get { return source; }
            set { source = value; }
        }

        /// <summary>
        /// Gets or sets the destination vertex.
        /// </summary>
        /// <value>The destination vertex.</value>
        [Browsable(false), XmlIgnore]
        public LCVertex Destination
        {
            get { return destination; }
            set { destination = value; }
        }

        /// <summary>
        /// Gets or sets the name of the source vertex.
        /// </summary>
        /// <value>The name of the source vertex.</value>
        [Browsable(false)]
        public string SourceName
        {
            get
            {
                if (Source != null) return Source.Name;
                return sourceName;
            }
            set { sourceName = value; }
        }

        /// <summary>
        /// Gets or sets the name of the destination vertex.
        /// </summary>
        /// <value>The name of the destination vertex.</value>
        [Browsable(false)]
        public string DestinationName
        {
            get
            {
                if (Destination != null) return Destination.Name;
                return destinationName;
            }
            set { destinationName = value; }
        }

        /// <summary>
        /// Gets or sets the label.
        /// </summary>
        /// <value>The label.</value>
        [Category("Edge")]
        [Description("The label used to show more information about the edge.")]
        public string Label
        {
            get { return label; }
            set { label = value; }
        }

        /// <summary>
        /// Gets or sets the font.
        /// </summary>
        /// <value>The font.</value>
        [XmlIgnore]
        [Category("Edge")]
        [Description("The font used to display text for the edge.")]
        public Font Font
        {
            get { return font; }
            set { font = value; }
        }

        /// <summary>
        /// Gets or sets the color of the line.
        /// </summary>
        /// <value>The color of the line.</value>
        [XmlIgnore]
        [Category("Edge")]
        [DisplayName("Line Color")]
        [Description("The color used to draw the edge.")]
        public Color LineColor
        {
            get { return lineColor; }
            set { lineColor = value; }
        }

        /// <summary>
        /// Gets or sets the color of the label.
        /// </summary>
        /// <value>The color of the label.</value>
        [XmlIgnore]
        [Category("Edge")]
        [DisplayName("Label Color")]
        [Description("The color used to display the label of the edge.")]
        public Color LabelColor
        {
            get { return labelColor; }
            set { labelColor = value; }
        }

        /// <summary>
        /// Gets or sets the weight.
        /// </summary>
        /// <value>The weight.</value>
        [Category("Edge")]
        [Description("The weight of the edge.")]
        public double Weight
        {
            get { return weight; }
            set { weight = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="LCEdge"/> is directed.
        /// </summary>
        /// <value><c>true</c> if directed; otherwise, <c>false</c>.</value>
        [Category("Edge")]
        [Description("Specifies whether the edge is directed.")]
        public bool Directed
        {
            get { return directed; }
            set { directed = value; }
        }

        /// <summary>
        /// Gets or sets the style.
        /// </summary>
        /// <value>The style.</value>
        [Category("Dash Style")]
        [Description("Specifies the style of the edge.")]
        public DashStyle Style
        {
            get { return style; }
            set { style = value; }
        }

        /// <summary>
        /// Gets or sets the size of the arrow.
        /// </summary>
        /// <value>The size of the arrow.</value>
        [Category("Edge")]
        [DisplayName("Arrow Size")]
        [Description("The size of the arrow when the edge is directed.")]
        public int ArrowSize
        {
            get { return arrowSize; }
            set { arrowSize = value; }
        }

        /// <summary>
        /// Gets or sets the label movement.
        /// </summary>
        /// <value>The move label movement.</value>
        [Category("Edge")]
        [DisplayName("Label Movement")]
        [Description("Defines the label movement of the edge.")]
        public float MoveLabel
        {
            get { return moveLabel; }
            set { moveLabel = value; }
        }

        /// <summary>
        /// Gets or sets the XML font.
        /// </summary>
        /// <value>The XML font.</value>
        [Browsable(false)]
        public XmlFont XMLFont
        {
            get { return new XmlFont(Font); }
            set { Font = value.ToFont(); }
        }

        /// <summary>
        /// Gets or sets the XML color of the line.
        /// </summary>
        /// <value>The XML color of the line.</value>
        [Browsable(false)]
        public string XMLLineColor
        {
            get { return XmlDataConverter.SerializeColor(LineColor); }
            set { LineColor = XmlDataConverter.DeserializeColor(value); }
        }

        /// <summary>
        /// Gets or sets the XML color of the label.
        /// </summary>
        /// <value>The XML color of the label.</value>
        [Browsable(false)]
        public string XMLLabelColor
        {
            get { return XmlDataConverter.SerializeColor(LabelColor); }
            set { LabelColor = XmlDataConverter.DeserializeColor(value); }
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Determines whether this edge contains the specified vertex.
        /// </summary>
        /// <param name="vertex">The vertex.</param>
        /// <returns>
        /// 	<c>true</c> if this edge contains the specified verte; otherwise, <c>false</c>.
        /// </returns>
        public bool Contains(LCVertex vertex)
        {
            return (Source == vertex) || (Destination == vertex);
        }

        #endregion

        #region Graphic methods

        /// <summary>
        /// Draws the edge on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        public void Draw(Graphics g)
        {
            DrawLine(g);
            DrawLabel(g);
            DrawArrow(g);
        }

        /// <summary>
        /// Draws the line on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        public void DrawLine(Graphics g)
        {
            Pen pen = new Pen(LineColor, 2);
            pen.DashStyle = Style;
            g.DrawLine(pen,
                Source.GetIntersectantPoint(Destination.Position),
                Destination.GetIntersectantPoint(Source.Position));
            pen.Dispose();
        }

        /// <summary>
        /// Draws the label on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        public void DrawLabel(Graphics g)
        {
            PointF p1 = Source.GetIntersectantPoint(Destination.Position);
            PointF p2 = Destination.GetIntersectantPoint(Source.Position);
            RectangleF rc = new RectangleF(Math.Min(p1.X, p2.X), Math.Min(p1.Y, p2.Y), Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y));            
            Brush brush = new SolidBrush(LabelColor);
            g.DrawString(string.Format(this.Label, this.Weight), Font, brush, (p1.X + p2.X) / 2 + MoveLabel, (p1.Y + p2.Y) / 2);
            brush.Dispose();
        }

        /// <summary>
        /// Draws the arrow on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        public void DrawArrow(Graphics g)
        {
            if (!Directed) return;
            float angle;
            PointF p1 = Destination.GetIntersectantPoint(Source.Position);
            PointF p2 = Source.GetIntersectantPoint(Destination.Position);            
            float dx = Math.Abs(p1.X - p2.X);
            float dy = Math.Abs(p1.Y - p2.Y);
            float d = (float)Math.Sqrt(dx * dx + dy * dy);
            if (d == 0) return;
            angle = (float)(Math.Acos(dy / d) * 180 / Math.PI);
            if (p1.X < p2.X) angle = 360 - angle;
            if (p1.Y > p2.Y) angle = 180 - angle;
            Brush brush = new SolidBrush(LineColor);
            GraphicsState gs = g.Save();
            g.TranslateTransform(p1.X, p1.Y);
            g.RotateTransform(angle);
            Point[] points = new Point[4] {
                new Point(0, 0),
                new Point(-ArrowSize, 2 * ArrowSize),
                new Point(0, ArrowSize),
                new Point(ArrowSize, 2 * ArrowSize)
            };
            g.FillPolygon(brush, points);
            g.Restore(gs);
            brush.Dispose();
        }

        /// <summary>
        /// Draws the mark on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        public void DrawMark(Graphics g)
        {
            float radius = 3;
            PointF p1 = Source.GetIntersectantPoint(Destination.Position);
            PointF p2 = Destination.GetIntersectantPoint(Source.Position);
            Brush brush = new SolidBrush(Color.Red);
            g.FillEllipse(brush, p1.X - radius, p1.Y - radius, radius * 2, radius * 2);
            g.FillEllipse(brush, p2.X - radius, p2.Y - radius, radius * 2, radius * 2);
            brush.Dispose();
        }

        /// <summary>
        /// Draws the edge with alpha on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        /// <param name="alpha">The alpha.</param>
        public void DrawAlpha(Graphics g, int alpha)
        {
            Color alphaColor = Color.FromArgb(alpha, LineColor);
            Pen pen = new Pen(alphaColor, 1);
            g.DrawLine(pen,
                Source.GetIntersectantPoint(Destination.Position),
                Destination.GetIntersectantPoint(Source.Position));
            pen.Dispose();
            DrawLabel(g);
            DrawArrow(g);
        }

        #endregion

        #region Serialization

        /// <summary>
        /// Serializes the specified output.
        /// </summary>
        /// <param name="output">The output.</param>
        public void Serialize(TextWriter output)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(LCEdge));
            serializer.Serialize(output, this);
        }

        /// <summary>
        /// Deserializes the specified input.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns></returns>
        public static LCEdge Deserialize(TextReader input)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(LCEdge));
            return serializer.Deserialize(input) as LCEdge;
        }

        #endregion
    }
}
